Webpack i18n plugin
🌍 webpack-i18n-assets-plugin
Webpack 플러그인으로, 지역화 함수(i18n) 호출을 대상 텍스트로 대체합니다.
기능
- i18n 텍스트를 번들에 인라인 처리합니다 (최종 문자열에 매개변수 치환 포함).
- 단일 빌드에서 모든 로케일에 대한 에셋을 생성합니다.
- 이 플러그인은 프로덕션 빌드에서만 작동합니다!
- 지역화 함수 인수에 리터럴만 지원합니다 (템플릿 문자열 및 변수는 허용되지 않음).
📝 사용 방법
-
패키지를 설치합니다:
npm i -D @gravity-ui/webpack-i18n-assets-plugin
-
Webpack에 플러그인을 연결합니다 (
@gravity-ui/app-builder
예시):Webpack 설정 예시 (
webpack.config.js
):const {I18nAssetsPlugin} = require('@gravity-ui/webpack-i18n-assets-plugin'); // 예시. 지역화된 텍스트가 포함된 모든 파일을 읽어 이 매핑에 저장합니다. const locales = { en: {}, ru: {}, tr: {}, }; module.exports = { output: { filename: '[name].[locale].js', // filename에 [locale]이 필수입니다. }, plugins: [ new I18nAssetsPlugin({ locales }) ] }
각 로케일에 대한 에셋 매니페스트를 생성하려는 경우 예시 (
webpack.config.js
):const {applyPluginToWebpackConfig} = require('@gravity-ui/webpack-i18n-assets-plugin'); const locales = { en: {}, ru: {}, tr: {}, }; // 기존 webpack 설정 const webpackConfig = { plugins: [ ... ], ... }; // applyPluginToWebpackConfig를 사용하면 WebpackAssetsManifest 플러그인도 함께 연결되어 // 각 로케일에 대한 에셋 매니페스트를 생성합니다. module.exports = applyPluginToWebpackConfig(webpackConfig, {locales});
@gravity-ui/app-builder
를 사용하는 경우 예시:import type {ServiceConfig} from '@gravity-ui/app-builder'; import {applyPluginToWebpackConfig, Options} from '@gravity-ui/webpack-i18n-assets-plugin'; const locales = { en: {}, ru: {}, tr: {}, }; // applyPluginToWebpackConfig를 사용하면 WebpackAssetsManifest 플러그인도 함께 연결되어 // 각 로케일에 대한 에셋 매니페스트를 생성합니다. const config: ServiceConfig = { client: { webpack: (originalConfig) => applyPluginToWebpackConfig(originalConfig, {locales}), }, }
-
서버에서 에셋 매니페스트를 통한 동적 정적 파일 설정을 구성합니다 (
@gravity-ui/app-layout
예시):import {createRenderFunction, createLayoutPlugin} from '@gravity-ui/app-layout'; const renderLayout = createRenderFunction([ createLayoutPlugin({ manifest: ({lang = 'en'}) => { return `assets-manifest.${lang}.json`; }, publicPath: '/build/', }), ]); app.get((req, res) => { res.send( renderLayout({ title: 'Home page', pluginsOptions: { layout: { name: 'home', }, }, }), ); });
🔧 설정
기본적으로 이 플러그인은 @gravity-ui/i18n
라이브러리와 함께 작동하도록 구성되어 있지만, 다른 i18n 라이브러리에 대한 처리를 사용자 정의할 수 있습니다.
importResolver
타입: ImportResolver
가져오기(import)를 처리하고 지역화 함수로 간주되어야 하는 가져오기(import)를 표시하는 함수입니다 (이후, 표시된 식별자에 대한 호출은 replacer에 의해 처리됩니다).
시그니처는 webpack의 원래 importSpecifier와 유사합니다.
예시:
const importResolver = (source: string, exportName: string, _identifierName: string, module: string) => {
// 특정 경로를 기반으로 모듈 처리를 무시해야 하는 경우, 이와 같이 처리할 수 있습니다.
if (module.startsWith('src/units/compute')) {
return undefined;
}
// 전역 함수의 기본 가져오기 처리
// import i18n from 'ui/utils/i18n'
if (source === 'ui/utils/i18n' && exportName === 'default') {
return {
resolved: true,
keyset: undefined,
};
}
// 헬퍼 함수의 가져오기 처리 및 공통 키셋(네임스페이스)에 속함을 지정합니다.
// import {ci18n} from 'ui/utils/i18n'
if (source === 'ui/utils/i18n' && exportName === 'ci18n') {
return {
resolved: true,
keyset: 'common',
};
}
return undefined;
};
declarationResolver
변수 선언을 처리하고 지역화 함수로 간주되어야 하는 변수를 표시하는 함수입니다 (이후, 표시된 식별자에 대한 호출은 replacer 함수에 의해 처리됩니다).
예시:
import type {VariableDeclarator} from 'estree';
const declarationResolver = (declarator: VariableDeclarator, module: string) => {
// 특정 경로를 기반으로 모듈 처리를 무시해야 하는 경우, 이와 같이 처리할 수 있습니다.
if (module.startsWith('src/units/compute')) {
return undefined;
}
<p>
<a href="README.md">English</a> |
<a href="README.ko.md">Korean</a>
</p>
// const i18nK = i18n.bind(null, 'keyset');
if (
declarator.id.type === 'Identifier' &&
declarator.id.name.startsWith('i18n') &&
declarator.init &&
isI18nBind(declarator.init)
) {
return {
functionName: declarator.id.name,
keyset: getKeysetFromBind(declarator.init),
};
}
return undefined;
};
replacer
Type: Replacer
지역화 함수 호출을 처리하고 문자열로 대체하는 함수입니다.
예시:
import type {VariableDeclarator} from 'estree';
import type {ReplacerArgs, ReplacerContext} from '@gravity-ui/webpack-i18n-assets-plugin';
function replacer(
this: ReplacerContext,
{callNode, key: parsedKey, keyset: parsedKeyset, localeName}: ReplacerArgs,
) => {
let key = parsedKey;
let keyset = parsedKeyset;
let params: Expression | SpreadElement | undefined;
const getStringValue = (node: Expression | SpreadElement) => {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
}
throw new Error('Incorrect argument type in localizer call');
};
// 인수가 하나인 호출 처리 i18nK('key')
if (callNode.arguments.length === 1) {
key = getStringValue(callNode.arguments[0]);
} else if (callNode.arguments.length === 2) {
// i18n('keyset', 'key') 또는 i18nK('key', {params}) 처리
const [firstArg, secondArg] = callNode.arguments;
// i18n('keyset', 'key') 호출
if (secondArg.type === 'Literal') {
keyset = getStringValue(firstArg);
key = getStringValue(secondArg);
} else {
// i18nK('key', {params}) 호출
key = getStringValue(firstArg);
params = secondArg;
}
} else if (callNode.arguments.length === 3) {
// i18n(namespace, key, params) 호출
const [firstArg, secondArg, thirdArg] = callNode.arguments;
keyset = getStringValue(firstArg);
key = getStringValue(secondArg);
params = thirdArg;
} else {
throw new Error('Incorrect count of arguments in localizer call');
}
// 함수 호출 인자에서 얻은 키를 반드시 처리해야 합니다.
// 함수가 키셋과 관련이 있다면, 코드를 수정한 후 키셋을 키에 삽입할 수 있습니다 (이것은 플러그인 기능입니다).
// ReplacerArgs에서 키를 사용하는 경우, 키셋 없이 제공되며 처리할 필요가 없습니다.
const keyParts = key.split('::');
if (keyParts.length === 2) {
key = keyParts[1];
}
const value = this.resolveKey(key, keyset);
// 필요에 따라 대체 옵션을 구현하세요.
// 예를 들어, 키가 복수형이면 함수 호출을 반환하는 등.
return JSON.stringify(value);
};
collectUnusedKeys
Type: [Boolean
] (기본값 - false)
프로젝트에서 사용되지 않는 키를 수집하는 모드를 활성화합니다. 빌드 후 unused-keys.json
이라는 파일이 생성됩니다.
정상적인 작동을 보장하기 위해 Replacer
함수에서는 항상 상세한 형식을 반환해야 합니다. 이는 대체 중에 자동으로 결정된 키와 키셋을 수정할 가능성이 있기 때문에 중요합니다.
프레임워크 설정
Gravity i18n
@gravity-ui/i18n
라이브러리의 지역화 함수 호출을 처리하는 함수입니다.
바로 사용할 수 있는 함수는 여기
에 있습니다.
함수가 작동할 코드 예시:
// importResolver는 ui/utils/i18n 경로의 기본 가져오기만 고려합니다.
import i18n from 'ui/utils/i18n';
// declarationResolver는 i18n.bind 호출을 값으로 갖는 변수를 처리합니다.
const i18nK = i18n.bind(null, 'component.navigation');
// replacer는 importResolver 및 declarationResolver에 의해 발견된 식별자에 대한 호출을 처리합니다.
// 즉, 다음 호출이 처리됩니다:
i18nK('some_key');
i18nK('some_plural_key', { count: 123 });
i18nK('some_key_with_param', { someParam: 'hello' });
i18n('component.navigation', 'some_key');
i18n('component.navigation', 'some_plural_key', { count: 123 });
i18n('component.navigation', 'some_key_with_param', { someParam: 'hello' });
Replacer는 추가로 다음을 수행합니다:
-
매개변수를 문자열로 인라인합니다. 예를 들어, 키 값이 다음과 같다고 가정해 봅시다:
const keyset = { some_key: 'string value with {{param}}' }; i18nK('some_key', {param: getSomeParam()}) // 대체 후 다음과 같이 됩니다: `string value with ${getSomeParam()}`
-
복수형 키에 대해 자체 호출 함수를 대체합니다:
const keyset = { pural_key: [ 'one_form {{count}}', 'few_form {{count}}', 'many_form {{count}}', 'other_form {{count}}', ], }; i18nK('pural_key', {count: getSomeCount()}) // 대체 후 다음과 같이 됩니다: (function(f,c){ const v=f[!c ? "zero" : new Intl.PluralRules("${locale}").select(c)]; return v && v.replaceAll("{{count}}",c); })({ "one": "one_form {{count}}", "few": "few_form {{count}}", "many": "many_form {{count}}", "other": "other_form {{count}}" }, getSomeCount())
ℹ️ FAQ
<p>
<a href="README.md">English</a> |
<a href="README.ko.md">한국어</a>
</p>
webpack-assets-manifest-localize와 비교하면 어떤가요?
이 플러그인을 구현하기 위해 webpack-localize-assets-plugins 패키지의 아이디어를 사용했습니다 (패키지 제작자에게 정말 감사합니다!).
차이점은 다음과 같습니다.
- 네임스페이스 헬퍼 (i18next의
useTranslation
등), 다른 모듈에서 가져온 함수 등 모든 종류의 국제화 함수와 함께 작업할 수 있는 더 편리한 API - 소스 코드에 상대적인 소스 맵의 올바른 생성
- webpack 5만 지원합니다. webpack 4 지원은 제거되었습니다.