국제화
국제화 (Internationalization)
레거시 문서
출처: 문서
본문
레거시 문서
이 섹션은 레거시 플러그인 문서의 일부예요. 새 프론트엔드 시스템 버전은 Internationalization을 참고하세요. i18n API(createTranslationRef, useTranslationRef)는 이전과 새 프론트엔드 시스템 모두에서 같은 방식으로 작동해요.
개요
Backstage 코어 기능은 플러그인과 앱을 위한 국제화를 제공해요. 기본 라이브러리인 i18next에 키에 대한 타입 안전성을 위한 Backstage의 추가 TypeScript 매직이 더해져 있어요.
플러그인 개발자용
플러그인을 만들 때 createTranslationRef를 사용해 플러그인의 모든 메시지를 정의할 기회가 있어요. 예를 들어:
import { createTranslationRef } from '@backstage/core-plugin-api/alpha';/** @alpha */export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { indexPage: { title: 'All your components', createButtonTitle: 'Create new component', }, entityPage: { notFound: 'Entity not found', }, },});
그런 다음 컴포넌트에서 이 메시지들을 이렇게 사용하세요.
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';const { t } = useTranslationRef(myPluginTranslationRef);return ( <PageHeader title={t('indexPage.title')}> <Button onClick={handleCreateComponent}> {t('indexPage.createButtonTitle')} </Button> </PageHeader>);
초기 딕셔너리 구조와 중첩이 점 표기법으로 변환되는 것을 보게 될 거예요. 따라서 키 이름에 camelCase를 쓰고 중첩 구조에 기대어 키를 분리하는 것을 권장해요.
i18n 메시지와 키에 대한 가이드라인
i18n 메시지와 키용 API는 꽤 유연한 API라서 제대로 만들기 꽤 까다로울 수 있어요. 플러그인 번역을 생각할 때 좋은 관행을 권장하는 시작 가이드를 정리했어요.
키 이름
메시지를 정의할 때는 번역의 의미론적 계층을 나타내는 중첩 구조를 사용하는 것을 권장해요. 이는 구조를 더 잘 조직하고 이해할 수 있게 해줘요. 예를 들어:
export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { dashboardPage: { title: 'All your components', subtitle: 'Create new component', widgets: { weather: { title: 'Weather', description: 'Shows the weather', }, calendar: { title: 'Calendar', description: 'Shows the calendar', }, }, }, entityPage: { notFound: 'Entity not found', }, },});
텍스트 콘텐츠 자체보다 콘텐츠의 의미론적 배치를 생각하세요. 관련 번역을 공통 접두사 아래에 그룹화하고, 중첩을 사용해 애플리케이션의 서로 다른 부분 사이의 관계를 나타내세요. 확장, 페이지 섹션, 시각적 범위나 경험 아래에서 그룹화를 시작하는 것이 좋아요.
번역은 가능하면 자체 텍스트 콘텐츠를 키로 사용하지 않아야 해요. 번역이 변경되면 혼란을 초래할 수 있기 때문이에요. 대신 텍스트의 위치나 용도를 설명하는 키를 사용하는 것을 선호하세요.
공통 키 이름
이 목록은 시간이 지나며 늘어나도록 의도되었지만, 아래는 가능하면 사용을 권장하는 일반적인 키 이름과 패턴의 몇 가지 예시예요.
${page}.title${page}.subtitle${page}.description${page}.header.title
키 재사용
여러 곳에서 같은 키를 재사용하는 것은 권장되지 않아요. 이는 모호함을 방지하고 각 키의 사용을 가능한 명확하게 유지하는 데 도움이 돼요. 대신 의미론적 섹션 아래에 그룹화된 중복 키를 만드는 것을 고려하세요.
평면 키
루트 수준에서 평면 키 구조를 피하세요. 이름 충돌로 이어질 수 있고 번역 파일을 관리하고 시간에 따라 진화시키기 어렵게 만들 수 있어요. 대신 번역을 공통 접두사 아래에 그룹화하세요.
export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { // this is BAD title: 'My page', subtitle: 'My subtitle', // this is GOOD dashboardPage: { header: { title: 'All your components', subtitle: 'Create new component', }, }, },});
복수형 (Plurals)
기본 구현으로 사용되는 i18next 라이브러리는 복수화에 대한 내장 지원이 있어요. 문서에서 설명된 대로 이 기능을 사용할 수 있어요.
이 기능을 사용하고 복수형 콘텐츠에 대해 다른 키 접두사를 만들지 않도록 권장해요. 예를 들어:
export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { dashboardPage: { title: 'All your components', subtitle: 'Create new component', cards: { title_one: 'You have one card', title_two: 'You have two cards', title_other: 'You have many cards ({{count}})', }, }, entityPage: { notFound: 'Entity not found', }, },});
JSX 요소
번역 API는 JSX 요소를 값으로 번역 함수에 직접 전달해 보간을 지원해요. 제공된 보간 값 중 어떤 것이 JSX 요소라면, 번역 함수는 문자열 대신 JSX 요소를 반환해요.
예를 들어 다음 메시지를 정의할 수 있어요.
메시지 정의
export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { entityPage: { redirect: { message: 'The entity you are looking for has been moved to {{link}}.', link: 'new location', }, }, },});
컴포넌트 안에서 이렇게 사용할 수 있어요.
컴포넌트 내에서 사용
const { t } = useTranslationRef(myPluginTranslationRef);return ( <div> {t('entityPage.redirect.message', { link: <a href="/new-location">{t('entityPage.redirect.link')}</a>, })} </div>);
바깥쪽 t 함수의 반환 타입은 JSX.Element이며, 기본 값은 메시지의 서로 다른 부분들의 React 프래그먼트예요.
애플리케이션 개발자용
앱 개발자로서 어느 플러그인의 기본 영어 메시지도 오버라이드하고, 추가 언어에 대한 번역을 제공할 수 있어요.
메시지 오버라이드
새 언어를 추가하지 않고 특정 메시지를 커스터마이즈하려면 기본 영어 메시지를 오버라이드하는 번역 리소스를 만드세요.
// packages/app/src/translations/catalog.tsimport { createTranslationResource } from '@backstage/frontend-plugin-api';import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';export const catalogTranslations = createTranslationResource({ ref: catalogTranslationRef, translations: { en: () => Promise.resolve({ default: { 'indexPage.title': 'Service directory', 'indexPage.createButtonTitle': 'Register new service', }, }), },});
그런 다음 앱에 등록하세요.
+ import { catalogTranslations } from './translations/catalog'; const app = createApp({+ __experimentalTranslations: {+ resources: [catalogTranslations],+ }, })
오버라이드하려는 키만 포함하면 돼요 — 누락된 키는 플러그인의 기본값으로 대체돼요.
언어 번역 추가
추가 언어에 대한 지원을 추가하려면 각 언어에 대해 지연 로드되는 메시지 파일이 있는 번역 리소스를 만드세요.
// packages/app/src/translations/userSettings.tsimport { createTranslationResource } from '@backstage/frontend-plugin-api';import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';export const userSettingsTranslations = createTranslationResource({ ref: userSettingsTranslationRef, translations: { zh: () => import('./userSettings-zh'), },});
번역 메시지는 타입 안전성을 위해 createTranslationMessages로 정의할 수 있어요.
// packages/app/src/translations/userSettings-zh.tsimport { createTranslationMessages } from '@backstage/frontend-plugin-api';import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';const zh = createTranslationMessages({ ref: userSettingsTranslationRef, full: false, // False means that this is a partial translation messages: { 'languageToggle.title': '语言', 'languageToggle.select': '选择{{language}}', },});export default zh;
또는 평범한 객체 export로:
// packages/app/src/translations/userSettings-zh.tsexport default { 'languageToggle.title': '语言', 'languageToggle.select': '选择{{language}}', 'languageToggle.description': '切换语言', 'themeToggle.title': '主题', 'themeToggle.description': '切换主题', 'themeToggle.select': '选择{{theme}}', 'themeToggle.selectAuto': '选择自动主题', 'themeToggle.names.auto': '自动', 'themeToggle.names.dark': '暗黑', 'themeToggle.names.light': '明亮',};
사용 가능한 언어를 선언해 등록하세요.
+ import { userSettingsTranslations } from './translations/userSettings'; const app = createApp({+ __experimentalTranslations: {+ availableLanguages: ['en', 'zh'],+ resources: [userSettingsTranslations],+ }, })
Settings 페이지로 가보세요 — 언어 전환 버튼이 보여야 해요. 번역이 올바르게 로드되었는지 언어를 전환해 확인하세요.
CLI로 전체 번역 워크플로우 사용
앱을 규모 있게 다른 언어로 번역할 때 — 특히 외부 번역 시스템과 함께 작업할 때 — Backstage CLI는 모든 플러그인 의존성에 걸쳐 번역 메시지의 추출과 배선을 자동화하는 translations export와 translations import 명령을 제공해요.
기본 메시지 내보내기
앱 패키지 디렉터리(예: packages/app)에서 다음을 실행하세요.
yarn backstage-cli translations export
이것은 모든 프론트엔드 플러그인 의존성(전이 의존성 포함)에서 TranslationRef 정의를 스캔하고 그들의 기본 영어 메시지를 JSON 파일로 씁니다.
translations/ manifest.json messages/ catalog.en.json org.en.json scaffolder.en.json ...
각 .en.json 파일은 평탄화된 메시지 키와 기본값을 담습니다.
{ "indexPage.title": "All your components", "indexPage.createButtonTitle": "Create new component", "entityPage.notFound": "Entity not found"}
번역 만들기
내보낸 파일을 복사해 대상 언어로 번역하세요.
cp translations/messages/catalog.en.json translations/messages/catalog.zh.json
그런 다음 번역된 문자열로 catalog.zh.json을 편집하세요. 번역하려는 키만 포함하면 돼요 — 누락된 키는 런타임에 영어 기본값으로 대체됩니다.
배선 코드 생성
번역된 파일이 준비되면 다음을 실행하세요.
yarn backstage-cli translations import
이것은 모든 것을 배선하는 src/translations/resources.ts의 TypeScript 모듈을 생성해요.
// This file is auto-generated by backstage-cli translations import// Do not edit manually.import { createTranslationResource } from '@backstage/frontend-plugin-api';import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';export default [ createTranslationResource({ ref: catalogTranslationRef, translations: { zh: () => import('../../translations/messages/catalog.zh.json'), }, }),];
앱에서 생성된 리소스를 import하세요.
import translationResources from './translations/resources';const app = createApp({ __experimentalTranslations: { availableLanguages: ['en', 'zh'], resources: translationResources, },});
사용자 지정 파일 패턴
기본적으로 메시지 파일은 messages/{id}.{lang}.json 패턴(예: messages/catalog.en.json)을 사용해요. --pattern 옵션으로 이것을 변경할 수 있어요.
yarn backstage-cli translations export --pattern '{lang}/{id}.json'
이것은 대신 언어별로 그룹화된 디렉터리 구조를 생성해요.
translations/en/catalog.jsontranslations/zh/catalog.json
패턴은 매니페스트에 저장되므로 import 명령이 자동으로 같은 레이아웃을 사용합니다.
외부 번역 시스템과의 통합
내보낸 JSON 파일은 대부분의 외부 번역 시스템과 호환되는 표준 키-값 쌍이에요. 일반적인 워크플로우는 이렇게 보여요.
translations export를 실행해 원본 영어 파일 생성.en.json파일을 번역 시스템에 업로드- 번역된 파일을 translations 디렉터리로 다시 다운로드
translations import를 실행해 배선 코드 재생성
전체 명령 참조는 CLI 명령 문서를 참고하세요.