다국어 카운트다운 구성 요소 구축

몇 년 전에 저는 여러 언어를 지원하는 카운트다운 구성 요소를 만들었습니다. 편집자는 단수 및 복수 버전 모두에서 각 언어에 대해 "일", "시간"및 "분"과 같은 레이블을 채워야 했습니다. 다행히 그 이후로 상황이 크게 개선되었습니다!

이 자습서에서는 언어별로 레이블을 채울 필요 없이 모든 언어에서 작동하는 바닐라 카운트다운 구성 요소를 구축할 것입니다.
Intl.RelativeTimeFormatIntl.NumberFormat API를 사용하여 다음과 같은 카운트다운 구성 요소를 빌드합니다.



그런 다음 단순히 lang -속성을 변경하여 다음과 같은 변형을 만듭니다.

lang="fa-IR"




lang="zh-Hans-CN-u-nu-hanidec"




lang="fr-FR"




멋지죠? 시작하자!


HTML


<time> -property에 설정된 종료 날짜와 함께 datetime -태그를 사용합니다.

<time datetime="2023-01-01"></time>


카운트다운이 특정 시간에 종료되도록 하려면 문자열에 추가하십시오.

<time datetime="2022-08-12T09:30:00+02:00"></time> 


NOTE: Remember to add the timezone offset of the location, where the countdown expires. This shouldn't be the server-time, as your location might be in central Europe, while your server/CDN is in the US. In the example above, the offset is +02:00.




자바스크립트



기본 요소( element )에 대한 참조인 <time> 단일 인수를 사용하여 새 함수를 만듭니다.

function countDown(element) { ... }


다음으로 일부 const 및 기본값을 설정합니다.

장소



로케일은 가장 중요한 부분입니다. 이 코드는 기본 요소에서 lang -attribute를 찾고, 찾지 못한 경우 페이지 자체에서 찾은 다음 마지막으로 en-US를 사용하여 대체 항목을 반환합니다.

const locale = element.lang || document.documentElement.getAttribute('lang') || 'en-US';


종료 날짜/시간



종료 시간은 시간으로 변환된 기본 요소의 datetime 속성입니다.

const endTime = new Date(element.getAttribute('datetime')).getTime();


아피스



마지막으로 Intl.RelativeTimeFormat의 인스턴스를 만들고 로케일 값이 0인 const를 저장합니다(자세한 내용은 나중에 설명합니다!).

const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
const zero = new Intl.NumberFormat(locale).format(0);



상영시간



이제 시간과 하위 부분을 반환하고 형식을 지정하는 코드의 경우:

const showTime = () => {
  const remainingTime = getRemainingTime(endTime);
  element.innerHTML = 
    timePart(Math.floor(remainingTime / (24 * 60 * 60 * 1000)), 'day') +
    timePart(Math.floor((remainingTime / (60 * 60 * 1000)) % 24), 'hour') +
    timePart(Math.floor((remainingTime / (60 * 1000)) % 60), 'minute') +
    timePart(Math.floor((remainingTime / 1000) % 60), 'second');
}


이 함수는 '종료 시간'에서 현재 시간을 빼는 도우미 메서드를 호출합니다.

const getRemainingTime = (endTime, currentTime = new Date().getTime()) => endTime - currentTime;


...뿐만 아니라 timePart 로케일 형식의 시간을 반환하는 가장 중요한 함수입니다.

const timePart = (part, type) => {
  const parts = rtf.formatToParts(part === 0 ? 2 : part, type);
  if (parts && parts.length === 3) parts.shift();
  const [unit, label] = parts; 
  return `<span><strong>${part === 0 ? zero : unit.value}</strong><small>${label.value}</small></span>`
}

formatToParts는 로케일 언어로 된 시간 단위 및 레이블의 배열을 반환합니다. 우리는 이것을 unitlabel 에 분산시키고 strong><small> -태그로 출력합니다(원하는 태그로 자유롭게 교체할 수 있습니다!).

requestAnimationFrame


showTime -함수는 자신을 지속적으로 호출해야 하며, 이를 위해 requestAnimationFrame를 사용합니다.

if (remainingTime >= 1000) requestAnimationFrame(showTime);



0은 복수



이제 값 자체가 formatToParts (0)인 경우 20로 호출하는 이유가 궁금할 것입니다. 그 이유는 Intl.RelativeTimeFormat가 값이 0이고 레이블이 없는 경우(로케일 언어로) 문자열 "now"를 반환하기 때문입니다(어떤 이유로든).

우리는 그것을 원하지 않지만 자체 0이 있는 언어에서 영어 0을 표시하고 싶지도 않습니다!

그래서 처음에 다음과 같이 선언했습니다.

const zero = new Intl.NumberFormat(locale).format(0);


레이블의 경우 1 보다 큰 값이 필요합니다. 예를 들어 "seconds"를 사용하면 값1은 레이블 "second"를 반환하고 값2은 "seconds"를 반환합니다. 0은 복수형("0초"가 아니라 "0초"라고 말함)이므로 2 😄

혼란스러운? 나도 그랬다!


마지막으로 초기화하고 실행하려면:

requestAnimationFrame(showTime);



휴! 많은 코드가 있지만 약입니다. 축소 및 gzip 압축 시 400바이트!


CSS



CSS는 단순한 그리드입니다. 자세한 내용은 아래 데모를 참조하세요. 저는 변형이 있을 수 있는 구성 요소 부분에 CSS Custom Props를 사용하는 것을 좋아합니다. 내가 선호하는 형식은 [component]-[part]-[emmet 속성의 약어]입니다.

.variant {
--countdown-bgc: hsl(0, 35%, 45%);
--countdown-time-bgc: hsl(0, 35%, 80%);
--countdown-time-lbl-c: hsl(0, 35%, 15%);
--countdown-time-val-c: hsl(0, 35%, 25%);
}



데모



아래는 Codepen입니다. 자유롭게 포크하고 변경하십시오the locales.

좋은 웹페이지 즐겨찾기